package org.mv.mm.docs.nodes;

import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.Action;
import org.mv.mm.api.Doc;
import org.mv.mm.api.capabilities.IRefreshableDocViewCapability;
import org.mv.mm.api.capabilities.IRemovableDocCapability;
import org.mv.mm.api.data.IDataProvider;
import org.mv.mm.common.CentralLookup;
import org.mv.mm.docs.DocNewType;
import org.mv.mm.docs.actions.DocDetailAction;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.PropertySupport;
import org.openide.nodes.Sheet;
import org.openide.util.Utilities;
import org.openide.util.datatransfer.NewType;
import org.openide.util.lookup.AbstractLookup;
import org.openide.util.lookup.InstanceContent;

/**
 *
 * @author Martin
 */
public final class DocNode extends AbstractNode {

  private static final Logger logger = Logger.getLogger(DocNode.class.getName());
  private IDataProvider dataProvider;
  private DocNewType docType = null;
  public final static String ABBREV_PROPERTY = "ABBREV";

  /**
   * Constructor which calls the private method to create node for any doc.
   *
   * @param doc
   * @param dataProvider
   */
  public DocNode(Doc doc, IDataProvider dataProvid) {
    this(doc, dataProvid, new InstanceContent());
  }

  /**
   * Private constructor called by one of the public constructors, to prepare
   * parameters for calling parent (AbstractNode) constructor via super().
   *
   * @param doc
   * @param dataProvider
   * @param ic
   */
  private DocNode(final Doc doc, final IDataProvider dataProvider, InstanceContent ic) {
    super(Children.LEAF, new AbstractLookup(ic));

    this.dataProvider = dataProvider;
    final String oldTitle;

    oldTitle = doc.getWorkName();
    setShortDescription(doc.getDescription());


    docType = new DocNewType(dataProvider, false);
    ic.add(docType);

    ic.add(doc);
    ic.add(dataProvider);

    CentralLookup centralLookup = CentralLookup.getDefault();

    IRefreshableDocViewCapability rvc = new IRefreshableDocViewCapability() {

      @Override
      public void refreshChildren() throws Exception {


        setChildren(Children.create(new DocRootNodeChildFactory(DocNode.this.dataProvider), true));
      }

      @Override
      public void refreshAfterEdit(Doc document) throws Exception {

        if (document == doc) {
          String newTitle = doc.getWorkName(); // But it calls getHtmlDisplayName later, so the name is by those rules
          String newDescription = doc.getDescription();
          fireDisplayNameChange(oldTitle, newTitle);
          setShortDescription(newDescription);
        }
      }

      @Override
      public void refreshAfterDelete() throws Exception {
        // nothing yet
      }

      @Override
      public void refreshAfterNew() throws Exception {
        // nothing yet
      }

      @Override
      public boolean canRefresh() {
        return false; // At least not yet, maybe later some functionality.
      }
    };
    ic.add(rvc);
    centralLookup.add(rvc);
  }

  @Override
  public String getHtmlDisplayName() {
    Doc doc = getLookup().lookup(Doc.class);
    if (doc.getTitle() == null || doc.getTitle().isEmpty()) {
      return "<i>" + doc.getWorkName() + "</i>";
    } else {
      return doc.getTitle();
    }
  }

  /**
   * @Override public void setShortDescription() { Doc doc =
   * getLookup().lookup(Doc.class); if (doc.getDescription() == null ||
   * doc.getDescription().isEmpty()) { return doc.getDescription(); } else {
   * return super.getShortDescription(); }
  }
   */
  @Override
  public Action[] getActions(boolean context) {
    /*
     * List<Action> docActions = (List<Action>)
     * Utilities.actionsForPath("Actions/AnyNode");
     * docActions.addAll(Utilities.actionsForPath("Actions/DocNode"));
     */


    List<Action> docActions = (List<Action>) Utilities.actionsForPath("Actions/AnyNode");
    docActions.addAll(Utilities.actionsForPath("Actions/DocNode"));

    /*
     * docActions.add(CutAction.get(CutAction.class));
     * docActions.add(CopyAction.get(CopyAction.class));
     */
    /*
     * docActions.add(MoveUpAction.get(MoveUpAction.class));
     * docActions.add(MoveDownAction.get(MoveDownAction.class));
     */

    return docActions.toArray(
            new Action[docActions.size()]);
  }

  @Override
  public Action getPreferredAction() {
    return new Action() {//TODO vyřešit duplicitní kód tady a v DocDetail akci

      @Override
      public void actionPerformed(ActionEvent e) {
        DocDetailAction ddaction = new DocDetailAction(DocNode.this);
        ddaction.actionPerformed(e);
      }

      @Override
      public Object getValue(String key) {
        throw new UnsupportedOperationException("Not supported yet.");
      }

      @Override
      public void putValue(String key, Object value) {
        throw new UnsupportedOperationException("Not supported yet.");
      }

      @Override
      public void setEnabled(boolean b) {
        throw new UnsupportedOperationException("Not supported yet.");
      }

      @Override
      public boolean isEnabled() {
        return true;
      }

      @Override
      public void addPropertyChangeListener(PropertyChangeListener listener) {
        throw new UnsupportedOperationException("Not supported yet.");
      }

      @Override
      public void removePropertyChangeListener(PropertyChangeListener listener) {
        throw new UnsupportedOperationException("Not supported yet.");
      }
    };
  }

  /*
   * @Override public boolean canCut() { return true; }
   *
   * @Override public boolean canCopy() { return true; }
   */
  @Override
  public boolean canDestroy() {
    return true;
  }

  @Override
  public void destroy() throws IOException {
    Doc doc = getLookup().lookup(Doc.class);
    IDataProvider dataProv = getLookup().lookup(IDataProvider.class);
    IRemovableDocCapability rec =
            dataProv.getLookup().lookup(IRemovableDocCapability.class);

    try {
      rec.remove(doc);
    } catch (Exception e) {
      logger.log(Level.WARNING, null, e); //TODO provide a message from Bundle
    }

    CentralLookup cl = CentralLookup.getDefault();
    Collection<? extends IRefreshableDocViewCapability> nodes =
            cl.lookupAll(IRefreshableDocViewCapability.class);
    for (IRefreshableDocViewCapability rvc : nodes) {
      try {
        rvc.refreshAfterDelete();
      } catch (Exception ex) {
        logger.log(Level.WARNING, null, ex);
      }
    }

  }

  /**
   * Without this method, the NewAction does nothing on the node.
   *
   * @return DocNewType instance, which is called by action NewAction
   * (registered in layer.xml)
   */
  @Override
  public NewType[] getNewTypes() {
    return new NewType[]{docType};
  }

  /*
   * @Override public Transferable clipboardCut() throws IOException {
   * Transferable deflt = super.clipboardCut(); ExTransferable added =
   * ExTransferable.create(deflt); added.put(new
   * ExTransferable.Single(FragmentFlavor.CHAPTER_FLAVOR) {
   *
   * @Override protected Fragment getData() { return
   * getLookup().lookup(Fragment.class); } }); return added; }
   *
   * @Override public Transferable clipboardCopy() throws IOException {
   * Transferable deflt = super.clipboardCopy(); ExTransferable added =
   * ExTransferable.create(deflt); added.put(new
   * ExTransferable.Single(FragmentFlavor.CHAPTER_FLAVOR) {
   *
   * @Override protected Fragment getData() { return
   * getLookup().lookup(Fragment.class); } }); return added; }
   */
  /**
   * This method is invoked once to create a Sheet of Properties of this node.
   *
   * @return A Sheet of Properties for this node.
   */
  @Override
  protected Sheet createSheet() {

    Sheet sheet = Sheet.createDefault();
    Sheet.Set set = Sheet.createPropertiesSet();

    // Read-only properties
    Property<String> docAbbrevProperty =
            new PropertySupport.ReadWrite<String>(ABBREV_PROPERTY, String.class, "Abbrev.", "The document title abbreviation (for easy orientation).") {

              @Override
              public String getValue() throws IllegalAccessException, InvocationTargetException {

                // Returns this node's entity abbrev.
                return getLookup().lookup(Doc.class).getAbbrev();

              }

              @Override
              public void setValue(String val) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
                getLookup().lookup(Doc.class).setAbbrev(val);
              }
            };

    set.put(docAbbrevProperty);

    // Add the set of properties to the sheet
    sheet.put(set);

    return sheet;
  }
}
